///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_DBUS_CMETHOD_H_
#define _BUOLA_DBUS_CMETHOD_H_

#include <buola/xml.h>
#include <buola/traits/tuple.h>
#include <buola/dbus/cmessage.h>

namespace buola { namespace dbus {

namespace detail {

inline bool add_signature_element(xml::CNode pNode,const std::string &pName,const char *pType,const char *pDirection)
{
    if(!pType) return false;
    xml::CNode lChild=pNode.NewChild("arg");
    lChild.SetAttrib("name",pName);
    lChild.SetAttrib("type",pType);
    lChild.SetAttrib("direction",pDirection);
    return true;
}

//template<typename... tArgs>
//inline void add_signature_args(CXMLNode pNode,int pNum=1);

template<typename... tArgs>
struct CSignatureArgsAdder;

template<>
struct CSignatureArgsAdder<>
{
    static void Add(xml::CNode /*pNode*/,int /*pNum*/)
    {}
};

template<typename tFirst,typename... tRest>
struct CSignatureArgsAdder<tFirst,tRest...>
{
    static void Add(xml::CNode pNode,int pNum)
    {
        if(add_signature_element(pNode,"arg"+to_string(pNum),
                            MSignature<ODecay<tFirst>>::Get(),"in"))
            pNum++;
        CSignatureArgsAdder<tRest...>::Add(pNode,pNum);
    }
};

template<typename... tArgs>
inline void add_signature_args(xml::CNode pNode)
{
    CSignatureArgsAdder<tArgs...>::Add(pNode,1);
}

template<typename tReturn>
inline void add_signature_return(xml::CNode pNode)
{
    add_signature_element(pNode,"ret",MSignature<ODecay<tReturn>>::Get(),"out");
}

template<>
inline void add_signature_return<void>(xml::CNode /*pNode*/)
{
}

class CMethodBase
{
public:
    virtual ~CMethodBase() {};
    virtual void Dispatch(CMessage *pM,void *pO)=0;
};

template<typename tSignature,typename tSub>
class CMethod;

template<typename tReturn,typename... tArgs,typename tSub>
class CMethod<tReturn(tArgs...),tSub> : public CMethodBase
{
public:
    typedef std::function<tReturn(tSub*,tArgs...)> tComplete;
    typedef std::tuple<ODecay<tArgs>...> tArgsTuple;

    CMethod(const tComplete &pF)
        :   mF(pF)
    {}

    static void AppendSignatureArgs(xml::CNode pNode)
    {
        add_signature_return<tReturn>(pNode);
        add_signature_args<tArgs...>(pNode);
    }

    template<typename tRet,std::size_t... pIndices>
    typename std::enable_if<std::is_same<void,tRet>::value,void>::type MakeFunctionCall(CMessage *pM,
                                             void *pO,const MIndexTuple<pIndices...>&,const tArgsTuple &pArgs)
    {
        mF((tSub*)pO,std::get<pIndices>(pArgs)...);
        PMessage lReply=pM->BuildReply();
        lReply->Send();
    }
    
    template<typename tRet,std::size_t... pIndices>
    typename std::enable_if<!std::is_same<void,tRet>::value,void>::type MakeFunctionCall(CMessage *pM,
                                             void *pO,const MIndexTuple<pIndices...>&,const tArgsTuple &pArgs)
    {
        tReturn lRet=mF((tSub*)pO,std::get<pIndices>(pArgs)...);
        PMessage lReply=pM->BuildReply();
        append_value(lReply.get(),lRet);
        lReply->Send();
    }

public:
    virtual void Dispatch(CMessage *pM,void *pO)
    {
        
        tArgsTuple lTuple;
        pM->ExtractTuple(lTuple);
        typedef typename MMakeIndices<sizeof...(tArgs)>::type tIndices; 
        MakeFunctionCall<tReturn>(pM,pO,tIndices(),lTuple);
    }

    tComplete mF;
};

/*namespace detail*/ }

/*namespace dbus*/ } /*namespace buola*/ }

#endif /* _BUOLA_CDBUSSIGNATURE_H_ */
